﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace HCore
{
    public class DelayUdpSocket : UdpSocket
    {
        private class DelayPacket
        {
            public readonly long delayReceiveTime;
            public readonly ReceivedPacket packet;

            public DelayPacket(long d, ReceivedPacket p)
            {
                delayReceiveTime = d;
                packet = p;
            }
        }
        
        private readonly List<DelayPacket> _delayPackets;
        private readonly Stopwatch _stopwatch;
        private readonly int _lostRate;
        private readonly int _rttMin;
        private readonly int _rttMax;
        private readonly Random _random;
        private readonly string _debugName;

        public DelayUdpSocket(int lostRate, int rttMin, int rttMax, string debugName)
        {
            _stopwatch = new Stopwatch();
            _stopwatch.Start();
            _random = new Random();
            _lostRate = lostRate / 2;
            _rttMin = rttMin / 2;
            _rttMax = rttMax / 2;
            _debugName = debugName;
            _delayPackets = new List<DelayPacket>();
        }

        public override ReceivedPacket ReceiveFrom()
        {
            long ElapsedMilliseconds = _stopwatch.ElapsedMilliseconds;
            if (_delayPackets.Count > 0)
            {
                DelayPacket p = _delayPackets[0];
                if (p.delayReceiveTime <= ElapsedMilliseconds)
                {
                    _delayPackets.RemoveAt(0);
                    return p.packet;
                }
            }

            while (true)
            {
                ReceivedPacket receivedPacket = base.ReceiveFrom();
                if (receivedPacket == null)
                {
                    break;
                }

                if (_random.Next(0, 100) < _lostRate)
                {
                    continue;
                }

                long delayReceiveTime = ElapsedMilliseconds + _random.Next(_rttMin, _rttMax);
                DelayPacket delayPacket = new DelayPacket(delayReceiveTime, receivedPacket);

                bool insert = false;
                for (int i = 0; i < _delayPackets.Count; i++)
                {
                    DelayPacket v = _delayPackets[i];
                    if (delayReceiveTime < v.delayReceiveTime)
                    {
                        _delayPackets.Insert(i, delayPacket);
                        insert = true;
                        break;
                    }
                }

                if (insert)
                {
                    continue;
                }

                _delayPackets.Add(delayPacket);
            }

            return null;
        }
    }
}